package lims.bridge.remoteprint;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterJob;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;

import lims.client.LimsHTMLSource;
import lims.client.LimsPage;
import lims.client.LimsPrinter;
import lims.client.LimsService;
import lims.client.LimsService.LimsHttpResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.thrift.TException;
import org.fit.cssbox.css.CSSNorm;
import org.fit.cssbox.css.DOMAnalyzer;
import org.fit.cssbox.io.DOMSource;
import org.fit.cssbox.io.DefaultDOMSource;
import org.fit.cssbox.io.DocumentSource;
import org.fit.cssbox.layout.BrowserCanvas;
import org.fit.net.DataURLHandler;
import org.w3c.dom.Document;

import com.itextpdf.awt.AsianFontMapper;
import com.itextpdf.awt.PdfGraphics2D;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;

import bcm.printbridge.*;

public class RemotePrintBridgeService implements PrintBridge.Iface{
	public Log logger=LogFactory.getLog(getClass());
	
	private static RemotePrintBridgeService instance;
	public static RemotePrintBridgeService getInstance(){
		return instance;		
	}
	@Resource 
	private LimsService limsService;
	
	private String bridgeId;
	private String uid;
	private String password;
	private String serverAddress;
	private boolean kickedOut=false;
	public String bridgeRole="printBridge";
	
	public RemotePrintBridgeService(){
		if(instance==null) instance=this;
	}
	public String getBridgeId() {
		return bridgeId;
	}

	public void setBridgeId(String bridgeId) {
		this.bridgeId = bridgeId;
	}

	public String getUid() {
		return uid;
	}

	public void setUid(String uid) {
		this.uid = uid;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public PrintBridge.Processor<PrintBridge.Iface> getProcessor(){
		return new PrintBridge.Processor<PrintBridge.Iface>(this);
	}
	
	@Override
	public void ping() throws TException {
		logger.info("server pinging me.");		
		return;
	}

	public String getServerAddress() {
		return serverAddress;
	}

	public void setServerAddress(String serverAddress) {
		this.serverAddress = serverAddress;
	}

	@Override
	public void kickOut() throws TException {
		logger.debug("got kickout message...");	
		setKickedOut(true);
	}

	public boolean isKickedOut() {
		return kickedOut;
	}

	public void setKickedOut(boolean kickedOut) {
		this.kickedOut = kickedOut;
	}

	@Override
	public boolean checkPrinter(String printerName) throws PrintException,
			TException {
		
		boolean status=true;
		if(status&&printerName!=null){
			boolean printerExists=false;
			for(LimsPrinter printer:limsService.deviceTableModel){
				if(printerName.equals(printer.name)){
					printerExists=true;
					break;
				}
			}				
			//打印机是否存在
			if(!printerExists) throw new PrintException(-1,"请求的打印机未找到");			
		}
		if(status){
			limsService.initHttpClient();
			try {
				if(!limsService.reLogin()) {
					throw new PrintException(-1,"打印服务器未能登录到lims服务");
				}
			} catch (Exception e) {
				throw new PrintException(-1,"打印服务器请求lims的响应不正确");
			}
		}
		return status;
	}

	@Override
	public List<RemotePrinter> listPrinters() throws TException {
		List<RemotePrinter> ret=new ArrayList<>();
		for(LimsPrinter printer:limsService.deviceTableModel){
			RemotePrinter p=new RemotePrinter();
			p.name=printer.name;
			p.description=printer.description;
			ret.add(p);
		}				
		return ret;
	}

	@Override
	public String printPrinter(PrintParameters command) throws PrintException,TException {
		try{
			String docName=StringUtils.trimToEmpty(command.docName);
			String userName=StringUtils.trimToEmpty(command.userName);
			String printerName=StringUtils.trimToEmpty(command.printerName);
			String url=StringUtils.trimToEmpty(command.url);
			String uid=StringUtils.trimToEmpty(command.uid);

			if(userName.isEmpty()) userName="noname";
			if(docName.isEmpty()) docName="print";	
			
			Integer pageCount= doPrint(printerName,url,docName+","+userName,command.multiPage);

			Map<String,String> log=new HashMap<>();
			log.put("serverName",limsService.getServiceName());
			log.put("uid",uid);
			log.put("userName",userName);
			log.put("documentName",docName);
			log.put("printerName",printerName);
			log.put("documentType","print");
			log.put("pageCount",String.valueOf(pageCount));			
			limsService.addLog(log);				
								
			return "打印任务已接受";			
		}catch(Exception e){
			if(e.getMessage()!=null) throw new PrintException(-1,e.getMessage());
			throw new PrintException(-1,"打印失败");
		}
	}

	@Override
	public ByteBuffer printPdf(PrintParameters command) throws PrintException,TException {
		String docName=StringUtils.trimToEmpty(command.docName);
		String userName=StringUtils.trimToEmpty(command.userName);
		String printerName=StringUtils.trimToEmpty(command.printerName);
		String url=StringUtils.trimToEmpty(command.url);
		String uid=StringUtils.trimToEmpty(command.uid);

		if(userName.isEmpty()) userName="noname";
		if(docName.isEmpty()) docName="print";	
		try{
			List<Object> ret=doPdf(printerName,url,command.multiPage);
			byte[] pdf=(byte[]) ret.get(0);
			if(pdf==null) throw new Exception("无输出内容");
			Integer pageCount=(Integer) ret.get(1);
		
			Map<String,String> log=new HashMap<>();
			log.put("serverName",limsService.getServiceName());
			log.put("uid",uid);
			log.put("userName",userName);
			log.put("documentName",docName);
			log.put("printerName",printerName);
			log.put("documentType","pdf");
			log.put("pageCount",String.valueOf(pageCount));			
			limsService.addLog(log);
			
			return ByteBuffer.wrap(pdf);
		}catch(Exception e){
			if(e.getMessage()!=null) throw new PrintException(-1,e.getMessage());
			throw new PrintException(-1,"打印失败");
		}
	}

	@Override
	public ByteBuffer printImage(PrintParameters command) throws PrintException,TException {
		return null;
	}

	@Override
	public String printCommand(CommandPrintParameters parameters) throws PrintException, TException {
		try{
			String docName=StringUtils.trimToEmpty(parameters.docName);
			String userName=StringUtils.trimToEmpty(parameters.userName);
			String printerName=StringUtils.trimToEmpty(parameters.printerName);
			String uid=StringUtils.trimToEmpty(parameters.uid);
			String print_command = StringUtils.trimToEmpty(parameters.command);

			if(userName.isEmpty()) userName="noname";
			if(docName.isEmpty()) docName="print";

			Integer pageCount= doPrint(printerName,print_command);
			Map<String,String> log=new HashMap<>();
			log.put("serverName",limsService.getServiceName());
			log.put("uid",uid);
			log.put("userName",userName);
			log.put("documentName",docName);
			log.put("printerName",printerName);
			log.put("documentType","print");
			log.put("pageCount",String.valueOf(pageCount));
			limsService.addLog(log);
			return "打印任务已接受";
		}catch(Exception e){
			if(e.getMessage()!=null) throw new PrintException(-1,e.getMessage());
			throw new PrintException(-1,"打印失败");
		}
	}


	private BrowserCanvas createBrowserCanvas(DocumentSource docSource) throws Exception{
		URL pageURL=docSource.getURL();
		
		DOMSource parser = new DefaultDOMSource(docSource);
	    Document doc;
	    doc = parser.parse();
	    
		
	    DOMAnalyzer da = new DOMAnalyzer(doc,pageURL);
	    da.attributesToStyles(); //convert the HTML presentation attributes to inline styles
	    da.addStyleSheet(pageURL, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT); //use the standard style sheet
	    da.addStyleSheet(pageURL, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT); //use the additional style sheet
	    da.addStyleSheet(pageURL, CSSNorm.formsStyleSheet(), DOMAnalyzer.Origin.AGENT); //render form fields using css
	    da.addStyleSheet(pageURL, "body{background-color:#FFF;}", DOMAnalyzer.Origin.AGENT); //render form fields using css
	    da.getStyleSheets(); //load the author style sheets
	    return new BrowserCanvas(da.getRoot(),da,pageURL);
	}
	
	int doPrint(String printerName,String url,String jobName,boolean multiPage) throws Exception{
		URL baseURL=new URL(limsService.getServerURL());
		int pageCount=0;
		try{
			limsService.initHttpClient();
			if(!limsService.reLogin()) throw new Exception("内容服务器连接失败");
			
			LimsPrinter limsPrinter=limsService.deviceTableModel.getPrinter(printerName);
			if(limsPrinter==null) throw new Exception("输出设备未定义");
			PrintService service=limsPrinter.getPrintService();
			if(service==null) throw new Exception("输出设备不存在");
			PrintService printService=limsPrinter.getPrintService();
			if(printService==null) throw new Exception("打印机未设置");
						
			if(multiPage){
				for(int pageIndex=0;pageIndex<20;pageIndex++){
					URL pageURL=DataURLHandler.createURL(baseURL,url.concat("&pageNumber=").concat(String.valueOf(pageIndex)));
					logger.debug(String.format("checking wether page %d exists,url:%s",pageIndex,url));
			
					LimsHttpResponse response=LimsService.getInstance().httpGet(pageURL.toString());
			
					logger.debug(String.format("httpget status:%d,%s",response.statusCode,url));
					if(response.statusCode/100==4) {
						logger.debug(String.format("page %d not exists",pageIndex));
						break;
					}
					InputStream stream=response.content;
					String contentType=response.contentType;
					DocumentSource docSource=new LimsHTMLSource(baseURL,pageURL.toString(),stream,contentType);
					LimsPage page=new LimsPage(createBrowserCanvas(docSource));	
					page.scale=limsPrinter.scale;
					
					PrinterJob job = PrinterJob.getPrinterJob();
					job.setJobName(jobName.concat(String.format("第%d页",pageIndex=1)));
					job.setPrintService(service);	
					job.setPrintable(page);
				    job.print(limsPrinter.attributes);
				    pageCount+=page.pageCount;
				}
				return pageCount;
			}else {
				DocumentSource docSource = new LimsHTMLSource(baseURL,url);
				LimsPage page=new LimsPage(createBrowserCanvas(docSource));	
				page.scale=limsPrinter.scale;
				
				PrinterJob job = PrinterJob.getPrinterJob();
				job.setJobName(jobName);
				job.setPrintService(service);	
				job.setPrintable(page);
			    job.print(limsPrinter.attributes);
			    pageCount+=page.pageCount;
				return page.pageCount;
			}
		}finally{
			limsService.closeHttpClient();
		}
	}

	int doPrint(String printerName,String command) throws Exception{
		try{
			LimsPrinter limsPrinter=limsService.deviceTableModel.getPrinter(printerName);
			if(limsPrinter==null) throw new Exception("输出设备未定义");
			PrintService service=limsPrinter.getPrintService();
			if(service==null) throw new Exception("输出设备不存在");
			PrintService printService=limsPrinter.getPrintService();
			if(printService==null) throw new Exception("打印机未设置");

//			PrinterJob job = PrinterJob.getPrinterJob();
			DocPrintJob job = printService.createPrintJob();
			DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;

//			job.setJobName(jobName);
//			job.setPrintService(service);
			Doc doc=new javax.print.SimpleDoc(command.getBytes("GB2312"),flavor,null);
			job.print(doc,limsPrinter.attributes);
			return 1;
		}finally{
			limsService.closeHttpClient();
		}
	}
	
	List<Object> doPdf(String printerName,String url,boolean multiPage) throws Exception{
		Integer pageCount=0;
		URL baseURL=new URL(limsService.getServerURL());
		try{
			limsService.initHttpClient();
			if(!limsService.reLogin()) throw new Exception("内容服务器连接失败");
			LimsPrinter limsPrinter=limsService.deviceTableModel.getPrinter(printerName);
			if(limsPrinter==null) throw new Exception("输出设备未定义");
			PrintService service=limsPrinter.getPrintService();
			if(service==null) throw new Exception("输出设备不存在");
		
	    	PageFormat pageFormat = limsPrinter.getPageFormat();
	    	int width=(int)pageFormat.getWidth(),height=(int)pageFormat.getHeight();
	    	com.itextpdf.text.Document pdfDoc = new com.itextpdf.text.Document(new Rectangle(width,height));
	    	com.itextpdf.text.pdf.PdfWriter writer;
			try {			
				ByteArrayOutputStream bos = new ByteArrayOutputStream();    
				writer = com.itextpdf.text.pdf.PdfWriter.getInstance(pdfDoc, bos);
				pdfDoc.open();
		    	PdfContentByte contentByte = writer.getDirectContent();
		    	
		    	if(multiPage){		    			    	
			    	pageCount=0;
			    	for(int pageIndex=0;pageIndex<20;pageIndex++){
						URL pageURL=DataURLHandler.createURL(baseURL,url.concat("&pageNumber=").concat(String.valueOf(pageIndex)));
						logger.debug(String.format("checking wether page %d exists,url:%s",pageIndex,url));
				
						LimsHttpResponse response=LimsService.getInstance().httpGet(pageURL.toString());
				
						logger.debug(String.format("httpget status:%d,%s",response.statusCode,url));
						if(response.statusCode/100==4) {
							logger.debug(String.format("page %d not exists",pageIndex));
							break;
						}
						InputStream stream=response.content;
						String contentType=response.contentType;
						DocumentSource docSource=new LimsHTMLSource(baseURL,pageURL.toString(),stream,contentType);
						LimsPage page=new LimsPage(createBrowserCanvas(docSource));	
						page.scale=limsPrinter.scale;
						
						for(int i=0;i<20;i++){
							BufferedImage img=new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
					    	Graphics2D tmpg2d=img.createGraphics();
							if(page.print(tmpg2d, pageFormat,i)==Printable.NO_SUCH_PAGE) break;
							if(pageCount>0) pdfDoc.newPage();
							Graphics2D g2d = new PdfGraphics2D(contentByte,width, height,new AsianFontMapper(AsianFontMapper.ChineseSimplifiedFont,AsianFontMapper.ChineseSimplifiedEncoding_H));
							page.print(g2d, pageFormat,i);	
							g2d.dispose();						
							pageCount++;
						}
					}
					
		    	}else{
		    		DocumentSource docSource=new LimsHTMLSource(baseURL,url);
					LimsPage page=new LimsPage(createBrowserCanvas(docSource));	
					page.scale=limsPrinter.scale;
		    		for(int i=0;i<20;i++){
						BufferedImage img=new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
				    	Graphics2D tmpg2d=img.createGraphics();
						if(page.print(tmpg2d, pageFormat,i)==Printable.NO_SUCH_PAGE) break;
						if(pageCount>0) pdfDoc.newPage();
						Graphics2D g2d = new PdfGraphics2D(contentByte,width, height,new AsianFontMapper(AsianFontMapper.ChineseSimplifiedFont,AsianFontMapper.ChineseSimplifiedEncoding_H));
						page.print(g2d, pageFormat,i);	
						g2d.dispose();						
						pageCount++;
					}		    		
		    	}	    	
				
				pdfDoc.close();
				writer.close();
				List<Object> ret=new ArrayList<Object>();
				ret.add(bos.toByteArray());
				ret.add(pageCount);
				return ret;
			} catch (Exception e) {
				e.printStackTrace();
				throw(e);
			} 
		}finally{
			limsService.closeHttpClient();
		}
	}
	
	
	@Resource
	public void setLimsService(LimsService service) {
		this.limsService=service;
	}
}
